<?php

namespace Faed\LaravelAuthDoc\services;

use Faed\LaravelAuthDoc\DocParser;
use Exception;
use Faed\LaravelAuthDoc\models\Param;
use Faed\LaravelAuthDoc\models\Project;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Routing\Route;
use Closure;
use Illuminate\Routing\Router;
use Illuminate\Support\Facades\Cache;
use ReflectionException;

class ApiService
{
    public $routes;

    /**
     * 返回数据
     * @var array
     */
    protected $recordReturn=[];

    /**
     * The router instance.
     *
     * @var Router
     */
    protected $router;

    /**
     * @var array
     */
    protected $columns = [];

    /**
     * @var array
     */
    protected $column = [];

    /**
     * @var array
     */
    public $analysis = [];
    /**
     * @var string[]
     */
    protected $fun = ['index' => '列表 :value (详细)', 'store' => '新建 :value', 'show' => '单条查看 :value', 'update' => '修改 :value', 'destroy' => '删除 :value', 'list' => '列表 :value (简短)'];


    public function __construct(Router $router)
    {
        $this->router = $router;
        //加载公共注释
        $this->setColumns(Param::getProjectParam(Project::getProjectId()));

        $this->recordReturn = Cache::get('record-return',[]);
    }

    /**
     * @param array $columns
     */
    public function setColumns(array $columns): void
    {
        $this->columns = array_merge($this->columns, $columns);
    }

    public function refactoringColumn($tables = [])
    {
        if (!count($tables)) {
            return;
        }
        $result = array_filter($this->columns, function ($value) use ($tables) {
            return in_array($value['table'], $tables);
        });
        $this->column = array_column($result, 'val', 'name');
    }

    public function setColumn(array $columns): void
    {
        $this->column = array_merge($this->columns, $columns);
    }
    public function getColumn($key): string
    {
        return $this->column[$key] ?? '';
    }


    /**
     * 获取全部路由
     * @param array $filter
     * @return ApiService
     */
    public function getApis(array $filter = [])
    {
        $this->routes = collect($this->router->getRoutes())->map(function ($route) {
            return $this->getRouteInformation($route);
        })->filter(function ($route) {
            if (strstr($route['action'], '@') === false) {
                return false;
            }
            return true;
        })->filter(function ($route) use ($filter) {
            return $this->filterApi($route['uri'], $filter);
        });
        return $this;
    }

    /**
     * 路由详细信息
     * @param $route
     * @return array
     */
    protected function getRouteInformation($route)
    {
        return [
            'domain' => $route->domain(),
            'method' => implode('|', $route->methods()),
            'uri' => $route->uri(),
            'name' => $route->getName(),
            'action' => ltrim($route->getActionName(), '\\'),
            'middleware' => $this->getMiddleware($route),
        ];
    }

    /**
     * Get before filters.
     *
     * @param Route $route
     * @return string
     */
    protected function getMiddleware($route)
    {
        return collect($this->router->gatherRouteMiddleware($route))->map(function ($middleware) {
            return $middleware instanceof Closure ? 'Closure' : $middleware;
        })->implode("\n");
    }

    /**
     * 过滤路由
     * @param $uri
     * @param $filter
     * @return bool
     * @throws Exception
     */
    public function filterApi($uri, $filter)
    {
        if (!is_array($filter)) {
            throw new Exception("[$filter]必须是一个数组");
        }
        foreach ($filter as $item) {
            if (strstr($uri, $item)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 解析相关数据
     * @return $this
     * @throws ReflectionException
     */
    public function analysis()
    {
        $this->analysis = $this->routes->map(function ($route) {

            list($controller, $function) = explode('@', $route['action']);
            list($classDoc, $apiDoc, $api) = $this->getDocAnnotation($controller, $function);

            //重构column
            $tables = [];
            array_push($tables, $classDoc['t'] ?? '', $apiDoc['t'] ?? '');
            $tables = array_filter(array_unique($tables));
            $this->refactoringColumn($tables);

            return [
                'name' => $this->getApiName(@$classDoc['group'], @$apiDoc['description'], $function),
                'tables'=>$tables??[],
                'group' => $classDoc['group'] ?? '未分配名称',
                'path' => $route['uri'],
                'method' => $route['method'],
                'return'=>$this->getReturnResult($route['method'].'_'.$route['uri']),
                'parameter' => array_merge( //处理请求参数
                    $this->getParamDoc(array_merge($this->getKeyParam($apiDoc, 'u'), $this->getUriParameter($route['uri']))),
                    $this->getParamDoc($this->getKeyParam($apiDoc,'q')),
                    $this->getParamDoc(array_merge($this->getKeyParam($apiDoc,'b'),$this->getRequest($api))),
                ),
            ];
        })->toArray();

        return $this;
    }

    /**
     * 获取类方法注解
     * @param $controller
     * @param $function
     * @return array
     * @throws ReflectionException
     */
    public function getDocAnnotation($controller, $function)
    {
        //反射类数据
        $reflection = (new \ReflectionClass($controller));
        //类注释
        $classDoc = $this->getDoc($reflection);
        //方法注释
        $api = $reflection->getMethod($function);
        $apiDoc = $this->getDoc($api);
        return [$classDoc, $apiDoc, $api];
    }

    /**
     * @param $uri
     * @return array
     */
    public function getUriParameter($uri)
    {
        $urls = array_filter(explode('/', $uri), function ($uri) {
            if (strstr($uri, '{') === false) {
                return false;
            }
            return true;
        });
        $data = [];
        foreach ($urls as $value) {
            if (strpos($value, '?') !== false){
                $is = false;
            }else{
                $is = true;
            }

            $data [] = [
                'name' => substr($value, 1, $is?-1:-2),
                'is_must' => $is?'y':'n',
                'desc' => '',
                'example' => '',
                'parameter_type' => 'int',
                'type'=>'u'
            ];
        }
        return $data;
    }

    /**
     * 读取公共配置
     * @param array $data
     * @return array
     */
    public function getParamDoc(array $data)
    {

        foreach ($data as &$datum) {
            if (empty($datum['desc'])) {
                $datum['desc'] = $this->getColumn($datum['name']);
            }
        }
        return $data;
    }


    public function getRequest($api)
    {

        foreach ($api->getParameters() as $value) {
            if ($value->getClass()) {
                $class = $value->getClass()->getName();
                $request = new $class();
                if ($request instanceof FormRequest) {
                    $rules = $request->rules();

                    if (method_exists($request, 'attributes')) {
                        $this->setColumn($request->attributes());
                    }
                    $type = 'b';
                    $data = [];
                    foreach ($rules as $key => $vv) {
                        $explode = explode('.', $key);
                        $explodeEnd = $explode[count($explode) - 1];
                        $name = $key;
                        $is_must = array_search('required', $vv) === false ? 'N' : 'Y';
                        $desc = @$this->columns[$explodeEnd];
                        $data[] = compact('name', 'is_must', 'desc','type');
                    }
                    return $data;
                }

            }
        }
        return [];
    }


    public function getApiName($classDescription, $apiDescription, $funName)
    {
        if ($apiDescription) {
            return $apiDescription;
        }
        $this->fun = array_merge($this->fun, config('doc.fun', []));
        if (array_key_exists($funName, $this->fun)) {
            return str_replace(':value', $classDescription, $this->fun[$funName]);
        }
        return '未分配名称';
    }


    public function getKeyParam($apiDoc, $key)
    {
        $data = [];
        $res = $apiDoc[$key] ?? [];
        foreach ($res as $vv) {
            $value = array_values(array_filter(explode(' ', $vv)));
            $data [] = [
                'name' => @$value[0],
                'is_must' => @$value[1] ?: 'n',
                'desc' => @$value[2],
                'example' => @$value[3],
                'parameter_type' => $value[4] ?? 'string',
                'type'=>$key
            ];
        }
        return $data;
    }

    /**
     * @param $query
     * @return array
     */
    public function getParam($query)
    {
        $query = (array)$query;
        $data = [];
        foreach ($query as $value) {
            $value = array_values(array_filter(explode(' ', $value)));
            $data [] = ['name' => @$value[0], 'is_must' => @$value[1] ?: 'N', 'desc' => @$value[2], 'example' => @$value[3]];
        }
        return $data;
    }

    public function getReturnResult($key)
    {
        return $this->recordReturn[$key]??'';
    }

    /**
     * 获取doc
     * @param $reflection
     * @return array
     */
    public function getDoc($reflection)
    {
        return (new DocParser())->parse($reflection->getDocComment());
    }


    public function send()
    {
        try {
            $client = new Client();
            $client->post(config('authdoc.send') . '/auth-doc/save/' . Project::getProjectId(), [
                'form_params' => $this->analysis
            ]);
        } catch (GuzzleException $e) {
            throw new Exception('发送数据失败:' . $e->getMessage());
        }
    }
}